package scm.erp.core.mybatis.interceptor;

import scm.erp.core.mybatis.interceptor.annotation.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;

@Slf4j
@Intercepts({@Signature(
        type = Executor.class,
        method = "update",
        args = {MappedStatement.class, Object.class})})
public class FieldsFillInterceptor implements Interceptor {
//    private static final String DELETE_FLAG_FIELD_NAME = "delete_flag";
//    private static final String CREATE_USER_FIELD_NAME = "createUser";
//    private static final String CREATE_TIME_FIELD_NAME = "createTIME";
//    private static final String UPDATE_USER_FIELD_NAME = "updateUser";
//    private static final String UPDATE_TIME_FIELD_NAME = "updateTime";

    private Field[] getAllFields(Object object) {
        Class<?> clazz = object.getClass();
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);
        return fields;
    }

    public void fillFields(Object parameter, SqlCommandType sqlCommandType) {
        if (SqlCommandType.INSERT == sqlCommandType) {
            Field[] fields = getAllFields(parameter);
            for (Field field : fields) {
                try {
                    if (field.getAnnotation(scm.erp.core.mybatis.interceptor.annotation.UUID.class) != null) {
                        field.setAccessible(true);
                        field.set(parameter,
                                java.util.UUID
                                        .randomUUID()
                                        .toString()
                                        .replace("-", ""));
                        field.setAccessible(false);
                    }
                    if (field.getAnnotation(DeleteFlag.class) != null) {
                        field.setAccessible(true);
                        field.set(parameter, 0);
                        field.setAccessible(false);
                    }
                    if (field.getAnnotation(CreateUser.class) != null) {
                        field.setAccessible(true);
                        field.set(parameter, "admin");
                        field.setAccessible(false);
                    }
                    if (field.getAnnotation(CreateTime.class) != null) {
                        field.setAccessible(true);
                        field.set(parameter, LocalDateTime.now());
                        field.setAccessible(false);
                    }
                } catch (Exception e) {
                    log.error("设置创建时间和创建人异常, 异常信息:", e);
                }
            }
        }
        if (SqlCommandType.UPDATE == sqlCommandType) {
            Field[] fields = getAllFields(parameter);
            for (Field field : fields) {
                try {
                    if (field.getAnnotation(UpdateUser.class) != null) {
                        field.setAccessible(true);
                        field.set(parameter, "admin");
                        field.setAccessible(false);
                    }
                    if (field.getAnnotation(UpdateTime.class) != null) {
                        field.setAccessible(true);
                        field.set(parameter, LocalDateTime.now());
                        field.setAccessible(false);
                    }
                } catch (Exception e) {
                    log.error("设置更新时间和创建人异常, 异常信息:", e);
                }
            }
        }
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        Object parameter = invocation.getArgs()[1];
        if (parameter == null) {
            return invocation.proceed();
        }
        if (parameter instanceof MapperMethod.ParamMap<?>) {
            MapperMethod.ParamMap<?> map = (MapperMethod.ParamMap<?>) parameter;
            Object obj = map.get("list");
            List<?> list = (List<?>) obj;
            if (list != null) {
                for (Object o : list) {
                    fillFields(o, sqlCommandType);
                }
            }

        } else {
            fillFields(parameter, sqlCommandType);
        }

        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

}
